package practice

import "fmt"

/*
	Go语言不是一门面向对象的语言，没有对象和继承，也没有面向对象的多态、重写相关特性,但可以变相实现
	Go所拥有的是数据结构(struct)，它可以关联方法。Go也支持简单但高效的组合(Composition)
*/

/*
	//http://www.findme.wang/blog/detail/id/559.html
	//方法集
	在go语言中，每个类型都有与之关联的方法，把这个类型的所有方法称为类型的方法集:
	1.类型 T 方法集(值类型)，包含全部 receiver T 方法
	2.类型 *T 方法集(引用类型)，包含全部 receiver T + *T 方法
	不论是以上哪一种,都能用值类型或pointer对象调用全部的方法(T或*T声明的),即类型调用自己声明的方法时不需要考虑方法集,编译器会自动转换

	本例中:
	类型(u User)的方法集仅包含Getinfo()方法
	类型(u *User)的方法集包含了Getinfo()方法和Setinfo()方法
*/

/*
	导出(export)问题:
	如果struct名称首字母是小写的，这个struct不会被导出。连同它里面的字段也不会导出，即使有首字母大写的字段名
	如果struct名称首字母大写，则struct会被导出，但只会导出它内部首字母大写的字段，那些小写首字母的字段不会被导出
	如果struct嵌套了，那么即使被嵌套在内部的struct名称首字母小写，也能访问到它里面首字母大写的字段
*/
type User struct {
	Name string //若首字母大写则该成员为公有成员(对外可见),否则是私有成员(对外隐藏)
	Age  int    `姓名` //tag属性用于"注释"各个字段.除了reflect包,正常的程序中都无法使用这个tag属性
	Desc string
}

//定义User struct的方法(值类型),(u User)是本方法的接收者(receiver)
//注意: 只要receiver定义成值类型的，无论是使用值类型的实例还是指针类型的实例，都是拷贝整个底层数据结构的，方法内部访问的和修改的都是实例的副本。所以，如果有修改操作，不会影响外部原始实例 !
//这里的变量u如果不使用可以不写,直接(User)
func (u User) Getinfo() string {
	return u.Name + "," + u.Desc
}

//定义User struct的方法(引用类型),(u *User)是本方法的接收者(receiver)
//注意: 只要receiver定义成指针类型的，无论是使用值类型的实例还是指针类型的实例，都是拷贝指针，方法内部访问的和修改的都是原始的实例数据结构。所以，如果有修改操作，会影响外部原始实例 !
//func (u *User) Setinfo() {
//	u.Name = "lala"
//	u.Desc = "sth"
//}

func (u User) Setinfo(n, d string) {
	//u.Name = "lala"
	//u.Desc = "sth"
	u.Name = n
	u.Desc = d
}

type Inner struct {
	in1 int
	in2 int
}

//嵌套(即把一个类型作为另外一个类型的匿名字段)
type Outer struct {
	ou1   int
	ou2   int
	int   //匿名字段.可以不用给名称,匿名字段的名称强制和类型相同
	Inner //也可以写成inner Inner,那么赋值的时候就得是o.Inner.in1,而不能o.int1直接赋值
}

//通过嵌套可变相实现继承.可通过嵌套自身实现递归struct;通过嵌套自己的指针类型来生成特殊的数据结构:链表或二叉树(双端链表)
func Main_inherit() {
	/************ struct的嵌套使用 ************/
	o := new(Outer)
	o.ou1 = 1
	o.ou2 = 2

	o.int = 3 //对匿名字段赋值
	o.in1 = 4 //对嵌套类型的字段赋值 或 o.Inner.int1
	o.in2 = 5 //对嵌套类型的字段赋值

	fmt.Println(o.ou1) // 1
	fmt.Println(o.ou2) // 2
	fmt.Println(o.int) // 3
	fmt.Println(o.in1) // 4
	fmt.Println(o.in2) // 5

	/************ 值类型与引用类型声明 ************/
	//p1 := new(User)	//指针类型的new实例,p1.Name等价于*p.Name
	//p2 := User{}		//值类型的new实例
}

/*
	//https://www.cnblogs.com/f-ck-need-u/p/9882315.html
	//struct的值和指针
	type person struct {
		name string
		age  int
	}

	func main() {
		//p1、p2、p3都是person struct的实例，这三个变量与person struct实例的指向关系如下：
		// 变量名      指针     数据对象(实例)
		//-------------------------------
		//p1(addr) -------------> { 0}
		//p2 -----> ptr(addr) --> { 0}
		//p3 -----> ptr(addr) --> { 0}

		p1 := person{}		//直接指向实例
		p2 := &person{}		//指向实例的指针,和p3等价
		p3 := new(person)	//指向实例的指针,和p2等价
		fmt.Println(p1)		//输出{ 0}
		fmt.Println(p2)		//输出&{ 0}
		fmt.Println(p3)		//输出&{ 0}
	}
*/
